package indi.conastin.textmodify;

import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import indi.conastin.textmodify.databse.RuleInfo;

public class HookEntrance implements IXposedHookLoadPackage {

    private XSharedPreferences globalSps;
    private XSharedPreferences packageSps;
    // 存放全部规则
    private ArrayList<RuleInfo> allRules = new ArrayList<>();
    // 存放键值对
    private HashMap<String, RuleInfo> methods = null;

    @Override
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (lpparam.packageName.equals("indi.conastin.textmodify")) {
            // hook checkModelActive
            XposedHelpers.findAndHookMethod("indi.conastin.textmodify.MainActivity", lpparam.classLoader, "checkModelActive", XC_MethodReplacement.returnConstant(true));
        } else {
            // 消除附加进程干扰（可能导致异常）
            if (lpparam.isFirstApplication) {
                // 移动+授权
                initRuleFile(lpparam.packageName);
                Log.d("TextModify", "【HookEntrance】 | packageName: " + lpparam.packageName + " | try to init file");
                // 初始化配置文件
                globalSps = new XSharedPreferences(lpparam.packageName, "global_TextModify");
                Log.d("TextModify", "【HookEntrance】 | globalFilePath: " + globalSps.getFile() + " | globalFileValues: " + globalSps.getAll());
                packageSps = new XSharedPreferences(lpparam.packageName, lpparam.packageName + "_TextModify");
                Log.d("TextModify", "【HookEntrance】 | packageFilePath: " + packageSps.getFile() + " | packageFileValues: " + packageSps.getAll());
                // 配置文件读取到globalRules和packageRules
                allRules.addAll(loadRuleList(globalSps));
                Log.d("TextModify", "【HookEntrance】 | allRules: " + allRules.toString());
                allRules.addAll(loadRuleList(packageSps));
                Log.d("TextModify", "【HookEntrance】 | allRules: " + allRules.toString());
                // hook text TextView.class
                XposedHelpers.findAndHookMethod(TextView.class, "setText", CharSequence.class, TextView.BufferType.class,
                        new XC_MethodHook() {
                            @Override
                            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                                super.beforeHookedMethod(param);
                                Log.d("TypeHook1", param.args[0].getClass().toString() + " | content:" + param.args[0].toString());
//                        CharSequence cs = (CharSequence) param.args[0];
                                if (param.args[0] != null) {
                                    if (param.args[0] instanceof String) {
                                        String origin = param.args[0].toString();
//                                String result = origin.replace("text", "1111");
//                                param.args[0] = result;
                                        param.args[0] = "String";
                                        Log.d("TextHook", "处理TextView字符串：" + origin + " | 替换为：" + param.args[0]);
                                    } else if (param.args[0] instanceof SpannableString) {
                                        String origin = param.args[0].toString();
//                                param.args[0] = new SpannableString("2222");
                                        SpannableStringBuilder spannableString = (SpannableStringBuilder) param.args[0];
                                        spannableString.replace(1, 4, "34.9");
                                        param.args[0] = spannableString;
                                        Log.d("SpannableStringHook", "处理SpannableString字符串：" + origin + " | 替换为：" + param.args[0]);
                                    } else if (param.args[0] instanceof SpannableStringBuilder) {
                                        String origin = param.args[0].toString();
                                        SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) param.args[0];
                                        spannableStringBuilder.replace(0, 2, "33");
//                                spannableStringBuilder.append("修改以后");
                                        param.args[0] = spannableStringBuilder;
                                        Log.d("SpannableStringBuilderHook", "处理SpannableStringBuilder字符串：" + origin + " | 替换为：" + param.args[0].toString());
                                    } else if (param.args[0] instanceof StringBuffer) {
                                        String origin = param.args[0].toString();
                                        StringBuffer stringBuffer = (StringBuffer) param.args[0];
                                        stringBuffer.replace(0, 2, "44");
                                        Log.d("StringBuffer", "处理StringBuffer字符串：" + origin + " | 替换为：" + param.args[0]);
                                    } else {
                                        String origin = param.args[0].toString();
                                        Log.d("NonHook", "发现未知的控件:[0]" + origin + " | [1]" + param.args[0].getClass());
                                    }
                                }
                            }
                        });
            }
        }
    }

    private void initRuleFile(String packageName) throws IOException {
        // 获取root
        Process process = Runtime.getRuntime().exec("su");
        OutputStream out = process.getOutputStream();
        String cmd =
                "cp -f /data/data/indi.conastin.textmodify/shared_prefs/global_TextModify.xml /data/data/indi.conastin.textmodify/shared_prefs/" +
                        packageName + "_TextModify.xml /data/data/" +
                        packageName + "/shared_prefs/ \n";
        out.write(cmd.getBytes(StandardCharsets.UTF_8));
        out.flush();
        cmd =
                "chmod 777 /data/data/" +
                        packageName + "/shared_prefs/global_TextModify.xml /data/data/" +
                        packageName + "/shared_prefs/" +
                        packageName + "_TextModify.xml \n";
        out.write(cmd.getBytes(StandardCharsets.UTF_8));
        out.flush();
        out.write("exit \n".getBytes(StandardCharsets.UTF_8));
        out.close();
        InputStream fis = process.getInputStream();
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);
        String line = br.readLine();
        while (line != null) {
            line = br.readLine();
        }
    }

    private ArrayList<RuleInfo> loadRuleList(XSharedPreferences sps) {
        // temp中介 ruleList存放成对的<o,n>列表
        ArrayList<RuleInfo> ruleList = new ArrayList<>();
        Map<String, RuleInfo> temp = new HashMap<>();
        Map<String, ?> map = sps.getAll();
        // map按<o,n>配对存至temp
        for (String key : map.keySet()) {
            // 排除num的干扰 在HookEntrance里面没什么用
            if (!key.equals("num")) {
//                Log.d("TextModify", "【HookEntrance】 | key: " + key + " | key.startsWith('o'): " + key.startsWith("o"));
                if (key.startsWith("o")) {
                    // o
//                    Log.d("TextModify", "【HookEntrance】 | key.substring(1): " + key.substring(1));
//                    Log.d("TextModify", "【HookEntrance】 | map.get(key): " + map.get(key).toString());
//                    Log.d("TextModify", "【HookEntrance】 | temp.get(key.substring(1)): " + temp.get(key.substring(1)));
                    if (temp.get(key.substring(1)) == null) {
                        temp.put(key.substring(1), new RuleInfo(map.get(key).toString(), ""));
                    } else {
                        temp.put(key.substring(1), new RuleInfo(map.get(key).toString(), temp.get(key.substring(1)).getNewText()));
                    }
//                    Log.d("TextModify", "【HookEntrance】 | temp: " + temp.toString());
                } else {
//                    Log.d("TextModify", "【HookEntrance】 | key.substring(1): " + key.substring(1));
//                    Log.d("TextModify", "【HookEntrance】 | map.get(key): " + map.get(key).toString());
//                    Log.d("TextModify", "【HookEntrance】 | temp.get(key.substring(1)): " + temp.get(key.substring(1)));
                    // n
                    if (temp.get(key.substring(1)) == null) {
                        temp.put(key.substring(1), new RuleInfo("", map.get(key).toString()));
                    } else {
                        temp.put(key.substring(1), new RuleInfo(temp.get(key.substring(1)).getOriginText(), map.get(key).toString()));
                    }
//                    Log.d("TextModify", "【HookEntrance】 | temp: " + temp.toString());
                }
            }
        }
//        Log.d("TextModify", "【HookEntrance】 | temp: " + temp.toString());
        // temp键值对转List至ruleList
        for (String key : temp.keySet()) {
            ruleList.add(temp.get(key));
        }
//        Log.d("TextModify", "【HookEntrance】 | ruleList: " + ruleList.toString());
        return ruleList;
    }
}
